package fantasma;

import fabricas.FabricaEstados;
import interfaces.IEstado;
import interfaces.IFantasma;
import interfaces.IPacman;

import laberinto.Casilla;
import utilidades.Constantes;
import utilidades.Vector;

public abstract class Fantasma implements IFantasma{
	protected Casilla casillaActual, casillaInicial;
	protected IEstado estado;
	protected String estrategia;
	
	public Fantasma(Casilla casillaInicial,String estrategia){
		this.casillaActual = casillaInicial;
		this.casillaInicial = casillaInicial;
		this.estrategia=estrategia;
		this.estado = FabricaEstados.crearEstadoCazador(this);
		this.casillaActual.agregarContenido(this);
	}
	
	public void cambiarCasillaOriginalPor(Casilla casillaInicial){
		this.casillaActual = casillaInicial;
		this.casillaInicial = casillaInicial;
		this.casillaActual.agregarContenido(this);
	}

	
	@Override
	public String obtenerEstrategia(){
		return estrategia;
	}
	
	public Fantasma(String estrategia){
		this.estrategia=estrategia;
		this.estado = FabricaEstados.crearEstadoCazador(this);
	}
	
	public void establecerCasilla(Casilla casillaInicial){
		this.casillaActual = casillaInicial;
		this.casillaInicial = casillaInicial;
	}
	
	public Casilla obtenerCasilla(){
		return casillaActual;
	}
	
	public void actualizar(){
		convertirEnPresa();
	}
	
	public void convertirEnPresa(){
		estado.transicionAPresa();	
	}
	
	public void vivir(IPacman pacman){
		estado.actualizarEstado();
		mover(pacman);
	}
	
	public Vector obtenerPosicion(){
		return casillaActual.obtenerPosicion();
	}
	
	public void cambiarEstado(IEstado nuevoEstado) {
		estado = nuevoEstado;
	}
	
	@Override
	public void interactuarCon(IPacman pacman) {
		  estado.interactuarCon(pacman);
	}
	
	public void desaparecerFantasmaMuerto(){
		casillaActual.sacarContenido(this);
	}
	
	public boolean moverseYAvisarSiHuboBifurcacion(Vector direccion){

		if (direccion.esIgualA(Constantes.NULO)){
			return false;
		}
		Casilla destino = casillaActual.obtenerDestino(direccion);
		
		if(existeCasillaYEsOcupable(destino)){
			moverACasilla(destino);
			if(casillaActual.esBifurcacion()){
				return true;
			}
		}
		return false;
	}	
	
	public Vector obtenerDireccionSiEstaDentroDelRangoDeVision(IPacman pacman) {
		if(pacman.esUnPacmanNulo())
			return Constantes.NULO;
		
		Casilla casillaPacman = pacman.obtenerCasilla();

		if(estaDentroDelRangoDeVision(casillaPacman,Constantes.DERECHA)) 
			return Constantes.DERECHA;

		if(estaDentroDelRangoDeVision(casillaPacman,Constantes.IZQUIERDA)) 
			return Constantes.IZQUIERDA;

		if(estaDentroDelRangoDeVision(casillaPacman,Constantes.ARRIBA)) 
			return Constantes.ARRIBA;

		if(estaDentroDelRangoDeVision(casillaPacman,Constantes.ABAJO)) 
			return Constantes.ABAJO;
		
		return Constantes.NULO;
	}

	private boolean existeCasillaYEsOcupable(Casilla casilla){
		return (casilla != null) && (casilla.esOcupable());
	}

	private boolean estaDentroDelRangoDeVision(Casilla casillaPacman,Vector direccion){
		int casillasAvanzadas = 0;
		Casilla casillaProxima = casillaActual;
		while(existeCasillaYEsOcupable(casillaProxima) && (casillasAvanzadas < obtenerAlcanceDeVision())){
			if(casillaProxima == casillaPacman)
				return true; 
			casillaProxima = casillaProxima.obtenerDestino(direccion);
			casillasAvanzadas++;
		}
		return false;
	}	
	
	public Vector obtenerDireccionAlAzar(Vector direccionAEvitar){
		return casillaActual.obtenerDireccionAlAzar(direccionAEvitar);
	}
	
	@Override
	public String converitEnString() {
		return "";
	}
	
	@Override
	public void volverACasillaInicial(){
		moverACasilla(casillaInicial);
	}
	
	private void moverACasilla(Casilla nuevaCasilla){
		casillaActual.sacarContenido(this);
		casillaActual = nuevaCasilla;
		casillaActual.agregarContenido(this);		
	}
	
	@Override
	public String obtenerEstado(){
		return estado.obtenerEstadoEnTexto();
	}
	
	public String obtenerSentido(){
		return estado.obtenerDireccionEnTexto();
	}
	
	public Vector obtenerDireccion(){
		return estado.obtenerDireccion();
	}
	
	public Vector obtenerUltimaDireccion() {
		return estado.obtenerUltimaDireccion();
	}
	
	public boolean estaMuerto(){
		return estado.estaMuerto();
	}
	
	public boolean esPresa(){
		return estado.esPresa();
	}
}
